package core.impl;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import Utils.CLogger;

import core.api.ClientAPI;
import core.api.FormeAPI;
import core.api.ResoAPI;

public class Reso extends UnicastRemoteObject implements ResoAPI{

	/**
	 * 
	 */
	private static final long serialVersionUID = 8134820746492346054L;

	private int lastId = 30;
	private final Map<Integer, ClientAPI> clients = Collections.synchronizedMap(new HashMap<Integer, ClientAPI>());
	private final Random rand = new Random();
	
	
	private CLogger cLogger;

	public Reso() throws RemoteException{
		super();
		cLogger = new CLogger("Reso");
		cLogger.getcLogger().info("Reso : initialitation");
	}

	private final ClientAPI getAndWait(final int id){
		ClientAPI client = clients.get(id);

		if(client==null)
			System.out.println("Erreur : Client "+id+" introuvable");

		try {
			Thread.sleep(250 + rand.nextInt(250));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		return client;
	}

	@Override
	public void acquire(final int source, final int dest) throws RemoteException {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					ClientAPI client = getAndWait(dest);
					if(client == null)
					{
						getAndWait(source).timeout(dest);
					}
					else
					{
						cLogger.getcLogger().info("acquire : "+source+"|"+dest);
						client.acquire(source, dest);
					}
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

	@Override
	public void confirmed(final int source, final int dest, final int idForm) throws RemoteException {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					ClientAPI client = getAndWait(dest);
					if(client == null)
						getAndWait(source).timeout(dest);
					else
					{
						cLogger.getcLogger().info("confirmed : "+source+"|"+dest+"|"+idForm);
						client.confirmed(source, dest, idForm);
					}
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}
	
	@Override
	public void release(final int source, final int dest) throws RemoteException {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					ClientAPI client = getAndWait(dest);
					if(client == null)
						getAndWait(source).timeout(dest);
					else
					{
						cLogger.getcLogger().info("release : "+source+"|"+dest);
						client.release(source, dest);
					}
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

	@Override
	public void sendToTrace(final int source, final int dest, final int idForm, final FormeAPI forme) throws RemoteException{
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					ClientAPI client = getAndWait(dest);
					if(client == null)
						getAndWait(source).timeout(dest);
					else
					{
						client.sendToTrace(source, dest, idForm, forme);
						cLogger.getcLogger().info("sendToTrace : "+source+"|"+dest+"|"+idForm+"|"+forme.toString());
					}
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

	@Override
	public synchronized void registerClient(final ClientAPI client) throws RemoteException{
		if(client.getId() < 0)
			client.setId(lastId++);

		for(int id : clients.keySet())
			client.addClient(id);

		for(ClientAPI otherClient : clients.values())
			otherClient.addClient(client.getId());

		clients.put(client.getId(),client);
		
		cLogger.getcLogger().info("registerClient : "+client.getId());
	}

	@Override
	public void setMaster(final int source, final int dest, final int master)
			throws RemoteException {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					ClientAPI client = getAndWait(dest);
					if(client != null)
					{
						client.setMaster(source, dest, master);
						cLogger.getcLogger().info("setMaster : "+source+"|"+dest+"|"+master);
					}
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (RemoteException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void getMaster(final int source, final int dest) throws RemoteException{
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					ClientAPI client = getAndWait(dest);
					if(client != null)
					{
						client.getMaster(source, dest);
						cLogger.getcLogger().info("getMaster : "+source+"|"+dest);
					}
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (RemoteException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

	@Override
	public void getForms(final int source, final int dest) throws RemoteException {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					ClientAPI client = getAndWait(dest);
					if(client != null)
					{
						client.getForms(source, dest);
						cLogger.getcLogger().info("getForms : "+source+"|"+dest);
					}
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (RemoteException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

	@Override
	public Set<Integer> getClients() throws RemoteException{
		return Collections.unmodifiableSet(clients.keySet());
	}

	@Override
	public void unregisterClient(final ClientAPI client) throws RemoteException{
		clients.remove(client.getId());	
		cLogger.getcLogger().info("unregisterClient : "+client.getId());
	}

	@Override
	public void callProtocol(final int source, final int dest, final String method, final Object ... objects) throws RemoteException {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					ClientAPI client = getAndWait(dest);
					if(client != null)
					{
						client.invokeProtocol(source, dest, method, objects);
						String tmp ="callProtocol : "+source+"|"+dest+"|"+method.toString();
						for(int i = 0; i < objects.length; i++)
						{
							tmp += "|"+objects[i].toString();
						}
						cLogger.getcLogger().info(tmp);
					}else{
						getAndWait(source).timeoutProtocol(dest);
					}
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (RemoteException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

	public CLogger getcLogger() {
		return cLogger;
	}

	public void setcLogger(CLogger cLogger) {
		this.cLogger = cLogger;
	}

}